#include <math.h>
#include <iostream>
#include <string>
#include <memory>

using std::cout;
using std::endl;
using std::string;
using std::unique_ptr;

//面向对象的设计原则：开放闭合原则
//对扩展开放，对修改关闭
//
//抽象类，作为接口使用
class Figure
{
public:
    //纯虚函数
    virtual void display() const = 0;
    virtual double area() const = 0;
};

class Rectangle
: public Figure
{
public:
    Rectangle(double length = 0, double width = 0)
    : _length(length)
    , _width(width)
    {
        cout << "Rectangle(double = 0, double = 0)" << endl;
    }

    void display() const override
    {
        cout << "Rectangle";
    }

    double area() const override
    {
        return _length * _width;
    }

    ~Rectangle()
    {
        cout << "~Rectangle()" << endl;
    }
private:
    double _length;
    double _width;
};

class Circle
: public Figure
{
public:
    Circle(double radius = 0)
    : _radius(radius)
    {
        cout << "Circle(double = 0)" << endl;
    }

    void display() const override
    {
        cout << "Circle";
    }

    double area() const override
    {
        return 3.14 * _radius *_radius;;
    }

    ~Circle()
    {
        cout << "~Circle()" << endl;
    }
private:
    double _radius;
};

class Triangle
: public Figure
{
public:
    Triangle(double a = 0, double b = 0, double c = 0)
    : _a(a)
    , _b(b)
    , _c(c)
    {
        cout << "Triangle(double = 0, double = 0, double = 0)" << endl;
    }

    void display() const override
    {
        cout << "Triangle";
    }

    double area() const override
    {
        //海伦公式
        double tmp = (_a + _b + _c)/2;

        return sqrt(tmp * (tmp - _a) * (tmp - _b) * (tmp - _c));
    }

    ~Triangle()
    {
        cout << "~Triangle()" << endl;
    }
private:
    double _a;
    double _b;
    double _c;
};

void func(Figure *pfig)
{
    pfig->display();
    cout << "的面积 : " << pfig->area() << endl;
}
//1、违反了开放闭合原则，代码不具备扩展性
//2、违反依赖导致原则,没有面向抽象编程
//3、一个工厂，可以创建多种类型的产品，所以就违背了单一职责原则

//简单工厂（静态工厂）模式：根据产品名字，然后使用Factory生产产品
//但是不用管产品的实现细节（该工厂可以生产非常多的产品）
class Factory
{
public:
    static Figure *create(const string &name)
    {
        if(name == "rectangle")
        {
            //将构造函数中的参数放在配置文件中
            //读取配置文件
            //解析配置文件
            //将所需要的数据读出来传到构造函数中
            return new Rectangle(10, 20);
        }
        else if(name == "circle")
        {
            //将构造函数中的参数放在配置文件中
            //读取配置文件
            //解析配置文件
            //将所需要的数据读出来传到构造函数中
            return new Circle(10);
        }
        else if(name == "triangle")
        {
            //将构造函数中的参数放在配置文件中
            //读取配置文件
            //解析配置文件
            //将所需要的数据读出来传到构造函数中
            return new Triangle(3, 4, 5);
        }
        else if(name == "")
        {

        }
        else if(name == "  ")
        {

        }
        else
        {
            return nullptr;
        }
    }
#if 0
    static Figure *createRectangle()
    {
        //将构造函数中的参数放在配置文件中
        //读取配置文件
        //解析配置文件
        //将所需要的数据读出来传到构造函数中
        return new Rectangle(10, 20);
    }
    
    static Figure *createCircle()
    {
        //将构造函数中的参数放在配置文件中
        //读取配置文件
        //解析配置文件
        //将所需要的数据读出来传到构造函数中
        return new Circle(10);
    }
    
    static Figure *createTriangle()
    {
        //将构造函数中的参数放在配置文件中
        //读取配置文件
        //解析配置文件
        //将所需要的数据读出来传到构造函数中
        return new Triangle(3, 4, 5);
    }
#endif
    
};
#if 0
Figure *createRectangle()
{
    //将构造函数中的参数放在配置文件中
    //读取配置文件
    //解析配置文件
    //将所需要的数据读出来传到构造函数中
    return new Rectangle(10, 20);
}

Figure *createCircle()
{
    //将构造函数中的参数放在配置文件中
    //读取配置文件
    //解析配置文件
    //将所需要的数据读出来传到构造函数中
    return new Circle(10);
}

Figure *createTriangle()
{
    //将构造函数中的参数放在配置文件中
    //读取配置文件
    //解析配置文件
    //将所需要的数据读出来传到构造函数中
    return new Triangle(3, 4, 5);
}
#endif
int main(int argc, char **argv)
{
    unique_ptr<Figure> prec(Factory::create("rectangle"));
    unique_ptr<Figure> pcir(Factory::create("circle"));
    unique_ptr<Figure> ptri(Factory::create("triangle"));

    func(prec.get());
    func(pcir.get());
    func(ptri.get());

    return 0;
}

